#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
模块管理器
统一处理项目文件和stub文件的模块解析
"""

from pathlib import Path
from typing import Dict, Optional, List, TYPE_CHECKING
import sys

if TYPE_CHECKING:
    from .ir.module import IRModule
    from .compiler import MCPyCompiler
    from .symbol_table import Symbol


class ModuleManager:
    """统一的模块管理器 - 使用现有编译器处理所有Python文件"""

    def __init__(self, compiler: "MCPyCompiler"):
        self.compiler = compiler
        self._module_cache: Dict[str, "IRModule"] = {}
        self._search_paths: List[Path] = []

        # 自动设置搜索路径
        self._setup_default_search_paths()

    def _setup_default_search_paths(self):
        """设置默认的搜索路径"""
        self.add_search_path(Path.cwd())

        from .stubs_manager import get_stubs_directories

        for stubs_dir in get_stubs_directories():
            self.add_search_path(Path(stubs_dir))

    def add_search_path(self, path: Path):
        """添加模块搜索路径（项目目录、stub目录等）"""
        path = path.resolve()
        if path not in self._search_paths:
            self._search_paths.append(path)
            # 同时添加到Python的搜索路径，方便动态导入
            if str(path) not in sys.path:
                sys.path.insert(0, str(path))

    def get_module(self, module_name: str) -> Optional["IRModule"]:
        """获取模块的IR，支持 builtin 和文件模块"""
        if module_name in self._module_cache:
            return self._module_cache[module_name]

        # 1. 检查是否是 builtin 模块
        if self.is_builtin_module(module_name):
            ir_module = self._create_builtin_module(module_name)
            self._module_cache[module_name] = ir_module
            return ir_module

        # 2. 查找文件系统中的模块
        module_file = self._find_module_file(module_name)
        if not module_file:
            return None

        # 3. 编译模块文件
        ir_module = self._compile_module_file(module_file, module_name)
        if ir_module:
            self._module_cache[module_name] = ir_module

        return ir_module

    def _find_module_file(self, module_name: str) -> Optional[tuple[Path, Path]]:
        """在搜索路径中查找模块文件，支持点号路径（如 gtest.assertions）"""
        for search_path in self._search_paths:
            # 将点号路径转换为文件系统路径
            module_path_parts = module_name.split(".")

            # 1. 查找 module/path.py 文件
            if len(module_path_parts) > 1:
                # 对于 gtest.assertions，查找 gtest/assertions.py
                module_file = search_path
                for part in module_path_parts:
                    module_file = module_file / part
                module_file = module_file.with_suffix(".py")

                if module_file.exists():
                    return module_file, search_path

            # 2. 查找 module_name.py 文件（单级模块）
            if len(module_path_parts) == 1:
                module_file = search_path / f"{module_name}.py"
                if module_file.exists():
                    return module_file, search_path

            # 3. 查找 module/path/__init__.py 目录
            init_file = search_path
            for part in module_path_parts:
                init_file = init_file / part
            init_file = init_file / "__init__.py"

            if init_file.exists():
                return init_file, search_path

        return None

    def _try_import_mcpy_extension(self, module_file: tuple[Path, Path]):
        """按约定导入模块的MCPy编译器扩展"""
        file_path, search_path = module_file

        # 1. 确保file_path是search_path的子路径，并获取相对路径
        try:
            relative_file_path = file_path.relative_to(search_path)
        except ValueError:
            raise ValueError(
                f"导入 mcpy 扩展失败: {file_path} 不是 {search_path} 的子路径"
            )

        # 2. 基于相对路径逐级向上查找_mcpy目录
        # 从文件所在目录开始，逐级向上到search_path
        current_path = relative_file_path.parent
        while current_path != Path("."):
            # 构建当前检查的目录路径
            mcpy_dir = search_path / current_path / "_mcpy"
            if mcpy_dir.exists():
                module_name = (
                    str(current_path / "_mcpy").replace("/", ".").replace("\\", ".")
                )
                self._import_mcpy_extension(module_name)
                # 导入成功，停止查找
                break

            # 向上一级目录：去掉路径的最后一节
            current_path = current_path.parent

    def _import_mcpy_extension(self, module_name: str):
        try:
            import importlib

            importlib.import_module(module_name)
        except (ImportError, ValueError) as e:
            raise ValueError(f"导入 mcpy 扩展失败: {module_name}: {e}")

    def _merge_imported_symbols(
        self, target_module: "IRModule", source_module: "IRModule", import_node
    ):
        """将源模块的符号合并到目标模块中（仅合并import语句指定的符号）"""
        try:
            # 确保符号缓存已构建
            if not target_module._symbol_cache_built:
                target_module.build_symbol_cache()
            if not source_module._symbol_cache_built:
                source_module.build_symbol_cache()

            # 只合并import语句中指定的符号
            if hasattr(import_node, "imported_symbols"):
                for symbol_name in import_node.imported_symbols:
                    if symbol_name in source_module._symbol_map:
                        target_module._symbol_map[symbol_name] = (
                            source_module._symbol_map[symbol_name]
                        )
        except Exception as e:
            print(f"Warning: Failed to merge imported symbols: {e}")

    def _compile_module_file(
        self, module_file: tuple[Path, Path], module_name: str
    ) -> Optional["IRModule"]:
        """分析模式构建IR，不生成代码，只提取符号信息"""
        from .passes import TypeInferencePass

        # 设置临时配置
        old_passes = self.compiler.config.get("custom_passes", None)
        pass_only = self.compiler.config.get("passes_only", False)
        project_name = self.compiler.config.get("project_name", "main")
        self.compiler.config["custom_passes"] = [TypeInferencePass()]
        self.compiler.config["passes_only"] = True
        self.compiler.config["project_name"] = module_name

        try:
            self._try_import_mcpy_extension(module_file)

            result = self.compiler.compile_file(module_file[0])
            return result.ir_module if result.success else None
        finally:
            self.compiler.config["custom_passes"] = old_passes
            self.compiler.config["passes_only"] = pass_only
            self.compiler.config["project_name"] = project_name

    def get_symbol(self, module_name: str, symbol_name: str) -> Optional["Symbol"]:
        """从指定模块获取符号"""
        ir_module = self.get_module(module_name)
        if ir_module:
            return ir_module.lookup_symbol(symbol_name)
        return None

    def invalidate_module(self, module_name: str):
        """失效模块缓存（用于开发时热重载）"""
        self._module_cache.pop(module_name, None)

    def clear_cache(self):
        """清空所有缓存"""
        self._module_cache.clear()

    def get_cached_modules(self) -> List[str]:
        """获取已缓存的模块列表"""
        return list(self._module_cache.keys())

    def is_builtin_module(self, module_name: str) -> bool:
        """检查是否是 builtin 模块

        检查 BUILTIN_CONFIG 中是否有该模块的配置（is_module=True）
        注意：这不会注册到全局符号表，只有 import 后才可用
        """
        from .builtin.builtin_table import BUILTIN_CONFIG, is_module_config

        config = BUILTIN_CONFIG.get(module_name)
        return config is not None and is_module_config(config)

    def _create_builtin_module(self, module_name: str) -> "IRModule":
        """创建 builtin 模块的虚拟 IRModule

        这个模块不对应实际文件，其符号来自 BUILTIN_CONFIG
        模块的函数会被注册到模块自己的符号表中，而不是全局符号表
        """
        from .ir.module import IRModule
        from pathlib import Path

        # 创建一个轻量级的虚拟 IRModule
        ir_module = IRModule(
            module_full_name=module_name,
            module_path=Path(""),
        )
        ir_module.set_extension_param("is_builtin_module", True)
        self._register_builtin_module_functions(ir_module, module_name)
        return ir_module

    def _register_builtin_module_functions(
        self, ir_module: "IRModule", module_name: str
    ):
        """将 builtin 模块的配置注册到模块的符号表中

        使用统一的注册机制，支持函数、类型、常量等所有配置
        """
        from .builtin.builtin_table import BUILTIN_CONFIG, register_builtin_config

        module_config = BUILTIN_CONFIG.get(module_name)
        if not module_config or not module_config.get("is_module"):
            return

        includes = register_builtin_config(
            ir_module.get_registry(),
            module_name,
            module_config,
            module_name,
        )

        for include in includes:
            ir_module.require_include(include)
